package edu.calpoly;

import java.lang.Thread.State;
import java.util.ArrayList;
import java.util.HashSet;

/**
 * Used as nodes to represent a move decision for use in part of a maximin tree.
 *
 */
public class Decision {
	 //Contains vertices.
	private int x, y;
	//A representation of what the board looks like after this move.
	private Board newb;
	// Heuristic to represent the score of White.(black is just the negative of it.)
	private int score;
	//is the move valid
	private boolean valid;
	//The array of stones that represents the board
	private Stone[][] brd;
	//The color this decision is made for
	private String color;
	// Heuristic to represent the score of the last child
	private int childScore;
	
	public Board getBoard(){
		return newb;
	}
	
	/**
	 * 
	 * @param color of the move
	 * @param x of the move
	 * @param y of the move
	 * @param oldBoard to be played on this board
	 */
	public Decision (String color, int x, int y, Board oldBoard) {
		newb = oldBoard.clone();
		this.x = x;
		this.y = y;
		this.color = color;
		childScore = 0;
		
		score = newb.placeStone(color, x, y);
		if(score < 0){
			valid = false;
			//score = 4 * newb.placeStone(color, x, y);
			//For now we don't care what the score is unless your the last one.
			//calculate_score(color);
		}
		else { 
			valid = true;
		}
	}
	public Decision (String color, Board oldBoard) {
		newb = oldBoard;
		this.color = color;

	}
	/**
	 * 
	 * @return true if the move given was valid
	 */
	public boolean isValid() {
		return valid;
	}
	
	/**
	 * 
	 * @return the score 
	 */
	public int score() {
		return score;//(color.compareToIgnoreCase("w") == 0 ? score : -score);
	}
	
	public void setScore(int value){
		score = value;
	}
	/**
	 * 
	 * @return the x value of the move
	 */
	public int x() {
		return x;
	}
	/**
	 * 
	 * @return the y value of the move
	 */
	public int y() {
		return y;
	}
	
	/**
	 * @return the last child's score
	 */
	public int getChildScore() {
		return childScore;
	}
	
	/**
	 * @param set the last child's score
	 */
	public void setChildScore(int childScore) {
		this.childScore = childScore;
	}
	
	public int calculate_score(String color){
		if (color.compareToIgnoreCase("w") == 0){
 		   score = 16 * actualT(Stone.WHITE); 
 		   score += 4 * looseT(Stone.WHITE);
 		   score += 32 * newb.getCappedBlacks();
 		   score -= 32 * newb.getCappedWhites();
 		   score -= 8 * actualT(Stone.BLACK);
 		   //score -= 2 * looseT(Stone.BLACK);
		}
		else{
         score = 16 * actualT(Stone.BLACK); 
         score += 4 * looseT(Stone.BLACK);
         score += 32 * newb.getCappedWhites();
         score -= 32 * newb.getCappedBlacks();
         score -= 8 * actualT(Stone.WHITE);
         //score -= 2 * looseT(Stone.WHITE);
		}
		
		return score;
	}
	
	/**
	 * Note: This is a really inefficient way of doing this. I know it's bad. This problem was a little too tricky for me to try to bust out in a weekend elegantly and efficiently.
	 * This is the first idea that popped into my head that I could make work. I'll try to keep thinking of a way to have a set of empty groups to be kept in board that just updates
	 * as moves are played. But thinking of a solution to splitting groups up is trickier than combining groups.
	 * 
	 * @param the color to calculate actual territory for.
	 *
	 * @return the number of intersections controlled by the specified color.
	 */
	private int actualT(final int color){
		int territory = 0;
		HashSet<Stone> theBrd = new HashSet<Stone>();
		ArrayList<Stone> fringe = new ArrayList<Stone>();
		ArrayList<Group> empGrps = new ArrayList<Group>();
		
		// Fills the ArrayList<Stone> with the stones of the board.
		for(int i = 0; i < newb.getBrdsize(); i++){
		   for(int j = 0; j < newb.getBrdsize(); j++){
		      theBrd.add(newb.getStone(i, j));
		   }
		}
		
		// Goes through the list that represents the board, checking for empty stones.
		// If empty, adds it to a new group, and its neighbors, if also empty, to the fringe.
		// Then the fringe is used to build the groups of empties.
		for(Stone s : theBrd){
		   if (s.isEmpty()){
			   boolean forgetaboutit = false;
			   for(Group tgrp: empGrps){
	               if (tgrp.contains(s)){
	            	   forgetaboutit = true;
	            	   break;
	               }
            	}
			   if(forgetaboutit){
				   continue;
			   }
		      Group grp = new Group(s);
		      empGrps.add(grp);
		      fringe.add(s);
		      while(!fringe.isEmpty()){
		         Stone st = fringe.remove(0);
		         grp.add(st);
		         for(int i = 0; i < 4; i++){
		            Stone temp = st.getNeighbor(i);
		            if (temp != null && temp.isEmpty()){
		            	if (!grp.contains(temp) && !fringe.contains(temp)){
		            	   fringe.add(temp);
		            	}
		            	/*for(Group tgrp: empGrps){
			               if (!tgrp.contains(temp) && !fringe.contains(temp)){
			            	   fringe.add(temp);
			               }
		            	}*/
		            }
		         }
		      }
		   }
		}
		
		for(int i = 0; i < empGrps.size(); i++){
		   Group emp = empGrps.get(i);
		   emp.updateSidesReverse();
		   if (color == Stone.WHITE){
		      if (emp.getSides() == emp.getWhiteNeighbors()){
		         territory += emp.size();
		      }
		   }else{
            if (emp.getSides() == emp.getBlackNeighbors()){
               territory += emp.size();
            }
		   }
		}
		
		return territory;
	}
	
	private int looseT(final int color){
		HashSet<Stone> loosely = new HashSet<Stone>();
		
		for(int x = 0; x < newb.getBrdsize(); x++){
			for(int y = 0; y < newb.getBrdsize(); y++){
				if(newb.getStone(x, y).getState() == color)
					loosely.addAll(looseOwn(newb.getStone(x, y)));
			}
		}
		
		return loosely.size();
	}
	
	// Radius around stone to check for loosely owned territory.
	private static final int SCOPE = 3;
	private HashSet<Stone> looseOwn(Stone stone){
		HashSet<Stone> list = new HashSet<Stone>();
		// Check in all 4 directions
		for(int side = 0; side < 4; side++){
			Stone cur = stone.getNeighbor(side);
			//Only go as far as SCOPE
			for(int far = 0; far < SCOPE; far++){
				if((cur == null || cur.getState() == stone.getState()) && far > 0){
					cur = stone;
					//The stones in between are captured;
					for(int i = 0; i < far; i++){
						cur = cur.getNeighbor(side);
						list.add(cur);
					}
				} else if(cur != null && cur.getState() == Stone.EMPTY){
					//Empty so lets keep going.
					cur = cur.getNeighbor(side);
					continue;
				}
				// We've reached a stone no point going further.
				break;
			}
		}
		return list;
	}
}
